package com.dyrnq.seckill.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dyrnq.seckill.entity.RedPacketInfo;
import com.dyrnq.seckill.entity.RedPacketRecord;
import com.dyrnq.seckill.mapper.RedPacketInfoMapper;
import com.dyrnq.seckill.mapper.RedPacketRecordMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class RedPacketService {
    // 最佳手气金额不能超过最大金额的90%
    public static final BigDecimal BEST_LUCK_PERCENT = new BigDecimal("0.9");

    // 单人每次最小抢到的金额，默认为1分钱
    public static final BigDecimal ONE_PERSON_MIN_DRAW_AMOUNT = new BigDecimal(1);

    private final RedPacketInfoMapper redPacketInfoMapper;
    private final RedPacketRecordMapper redPacketRecordMapper;

    @Transactional
    public void cleanAndRefresh(long red_packet_id, int total_amount, int total_packet) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("red_packet_id",red_packet_id);
        redPacketInfoMapper.delete(queryWrapper);
        redPacketRecordMapper.delete(queryWrapper);
        RedPacketInfo info = new RedPacketInfo();
        info.setCreateTime(LocalDateTime.now());
        info.setUpdateTime(LocalDateTime.now());
        info.setRedPacketId(red_packet_id);
        info.setTotalAmount(total_amount);
        info.setTotalPacket(total_packet);
        info.setRemainingPacket(info.getTotalPacket());
        info.setRemainingAmount(info.getTotalAmount());
        info.setUid(1410080418L);
        info.setVersion(0);
        redPacketInfoMapper.insert(info);
    }

    /**
     * 通过数据库乐观锁 version字段
     *
     * @param redPacketId
     * @param userId
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public int grabRedPacket_occ(Long redPacketId, Long userId) {
        RedPacketInfo info = null;
        int amount = 0;
        while (true) {
            info = redPacketInfoMapper.selectByRedPacketId(redPacketId);
            int remainPacket = info.getRemainingPacket();
            if (remainPacket <= 0) {
                throw new RuntimeException("红包枪完了");
            } else if (remainPacket == 1) {
                amount = info.getRemainingAmount();
            } else {
                // 代码出处 <https://www.cnblogs.com/lingyejun/p/15389021.html>
                // 最大不超过剩余金额的90%
                BigDecimal maxDrawAmount = new BigDecimal(info.getRemainingAmount()).multiply(BEST_LUCK_PERCENT).setScale(2, RoundingMode.UP);
                // 二倍均值法，使每个人的中奖金额都按均值概率分布
                BigDecimal doubleAverageAmount = new BigDecimal(info.getRemainingAmount()).divide(new BigDecimal(remainPacket), 2, RoundingMode.UP).multiply(new BigDecimal(2)).setScale(2, RoundingMode.UP);
                maxDrawAmount = doubleAverageAmount.compareTo(maxDrawAmount) < 0 ? doubleAverageAmount : maxDrawAmount;

                BigDecimal othersAllDrawMaxAmountBalance = new BigDecimal(info.getRemainingAmount()).subtract(maxDrawAmount.multiply(new BigDecimal(remainPacket)));
                BigDecimal minDrawAmount = othersAllDrawMaxAmountBalance.compareTo(ONE_PERSON_MIN_DRAW_AMOUNT) < 0 ? ONE_PERSON_MIN_DRAW_AMOUNT : othersAllDrawMaxAmountBalance;

                BigDecimal drawLuckAmount = (maxDrawAmount.subtract(minDrawAmount)).multiply(BigDecimal.valueOf(Math.random())).setScale(2, RoundingMode.UP);

                drawLuckAmount = drawLuckAmount.compareTo(minDrawAmount) < 0 ? minDrawAmount : drawLuckAmount;

                amount = drawLuckAmount.intValue();
            }

            int update = redPacketInfoMapper.decrById(info.getId(), amount, info.getVersion());

            if (update > 0) {
                break;
            }
        }

        RedPacketRecord record = new RedPacketRecord();

        record.setUid(userId);
        record.setRedPacketId(redPacketId);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        record.setAmount(amount);

        int result = redPacketRecordMapper.insert(record);
        return result;
    }


}
